package com.hyperf.core.snowflake.worker;

import cn.hutool.crypto.digest.DigestUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;

import java.util.Collections;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;

/**
 * @author lizhaoyang
 */
@SuppressWarnings("all")
public class RedisWorkerRegister extends AbstractWorkerRegister {

    private static final Logger logger = LoggerFactory.getLogger(RedisWorkerRegister.class);

    /**
     * 分隔符
     */
    private static final String KEY_SEPARATOR = "::";

    /**
     * workerId在redis中的缓存key前缀
     */
    private static final String WORKER_ID_CACHE_KEY_PREFIX = "snowflake" + KEY_SEPARATOR + "workerid";

    /**
     * 定时间隔时间
     */
    private static final long TIMER_DELAY = 30000L;

    /**
     * lua脚本删除key
     */
    private static final String DELETE_KEY_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

    /**
     * workerId在redis中的过期时间
     */
    private static final int WORKER_ID_CACHE_EXPIRE_TIME = 60;

    private StringRedisTemplate stringRedisTemplate;

    private Timer timer;

    private String cacheKey;

    private String cacheValue;

    public RedisWorkerRegister(String applicationName, StringRedisTemplate stringRedisTemplate) {
        super(applicationName);
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    protected boolean doRegister(int workerId) {
        cacheKey = getCacheKey(workerId);
        cacheValue = DigestUtil.md5Hex(Thread.currentThread().getName() + workerId);

        Boolean flag = stringRedisTemplate.opsForValue()
                .setIfAbsent(cacheKey, cacheValue, WORKER_ID_CACHE_EXPIRE_TIME, TimeUnit.SECONDS);

        if (flag) {
            startTimer();
        }

        return flag;
    }

    @Override
    protected void doDelete(int workerId) {
        String key = getCacheKey(workerId);
        if (cacheKey.equals(key)) {
            shutdownTimer();
            DefaultRedisScript<Long> script = new DefaultRedisScript<>(DELETE_KEY_SCRIPT, Long.class);
            Object execute = stringRedisTemplate.execute(script, Collections.singletonList(key), cacheValue);
        }
    }

    private void startTimer() {
        if (timer == null) {
            timer = new Timer();
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    if (stringRedisTemplate.hasKey(cacheKey)) {
                        stringRedisTemplate.expire(cacheKey, WORKER_ID_CACHE_EXPIRE_TIME, TimeUnit.SECONDS);
                    } else {
                        RedisWorkerRegister.super.registerWorker();
                    }
                }
            }, TIMER_DELAY, TIMER_DELAY);
        }
    }

    private void shutdownTimer() {
        if (timer != null) {
            timer.cancel();
        }
    }

    private String getCacheKey(int workerId) {
        return WORKER_ID_CACHE_KEY_PREFIX + KEY_SEPARATOR + applicationName + KEY_SEPARATOR + workerId;
    }
}
